Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASLayout] Revisit the flattening algorithm #395

Merged
merged 5 commits into from
Jun 29, 2017

Conversation

nguyenhuy
Copy link
Member

@nguyenhuy nguyenhuy commented Jun 27, 2017

I'm facing an issue that blocks the first release of Weaver. In order to inspect the whole layout element tree that contains both display nodes and layout specs, I have to tell all display nodes to skip flattening their calculated layout immediately and only do so when the layout is applied (#337). When it's time to flatten a layout tree, the tree contains unflattened calculated layouts of all nodes in the corresponding node tree. The flattening algorithm doesn't handle this use case well because it relies on isFlattened flag of each ASLayout object which is never turned on.

The fact that the algorithm relies on that flag also caused a problem with Yoga integration because a Yoga tree is always flattened, but we may forget to set the flag. If we take a step back, this flag is actually a "calculated" flag which means it is determined by other internal states and shouldn't be a stored property.

This PR aims to eliminate the flag completely. There was a performance concern last time @maicki tried to remove it (facebookarchive/AsyncDisplayKit#3039). I took extra steps to ensure that we're now reusing ASLayout objects aggressively.

Besides the flag elimination, there are some other changes:

  • The algorithm no longer checks context.layout against self.
  • The (DFS) algorithm stops diving down a branch when it hits a display node (hence the if-else clause here). This is to make sure that, even if a sublayout is not flattened, no subnode of that sublayout is ever considered a direct subnode of the root node.
  • Re-enforce the requirement that a flattened layout tree is always 1-level deep. That means rootLayout.sublayouts[i].sublayouts.count is always 0. (Change here)
  • Add unit tests.

- Remove flattened flag
- No more self check
- Stop traversing a layout tree branch when hits a displaynode node.
- Reuse as many existing ASLayout objects as possible
Copy link
Member

@garrettmoon garrettmoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel like I have enough context to approve. I like the direction but am concerned about performance tradeoffs that were raised previously so will defer to those commenters.

@@ -207,7 +215,10 @@ - (void)setRetainSublayoutLayoutElements:(BOOL)retainSublayoutLayoutElements

- (ASLayout *)filteredNodeLayoutTree
{
NSMutableArray *flattenedSublayouts = [NSMutableArray array];
if (ASLayoutIsFlattened(self)) {
self.retainSublayoutLayoutElements = YES;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a strange side effect. Should we document why this needs to be the case?

if (ASLayoutIsDisplayNodeType(layout)) {
if (sublayoutsCount > 0 || CGPointEqualToPoint(absolutePosition, layout.position) == NO) {
// Only create a new layout if the existing one can't be reused, which means it has either some sublayouts or an invalid absolute position.
layout = [ASLayout layoutWithLayoutElement:layout.layoutElement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the "aggressive reuse"? Is this case skipped often enough in practice to make a difference? Not sure of the performance impact of removing the flag but do we have a good guess as to the consequences of landing this PR?

@ghost
Copy link

ghost commented Jun 28, 2017

🚫 CI failed with log

@nguyenhuy
Copy link
Member Author

nguyenhuy commented Jun 28, 2017

@garrettmoon Good points!

I run this diff against Pinterest master and got some numbers. It was able to reuse more than 140 ASLayout objects on app startup alone and easily more than 1000 after a short session. And that's fewer objects to dealloc later on.

Regarding performance impact of this diff, turns out it is 20-25% faster than the current implementation. Since each run is already very fast (less than 0.1ms) and can occur on background, the performance impact on each run is not noticeable, but can reach 100ms after ~7000 runs. That being said, I'm happy that this diff is not slower and any performance gain is just nice-to-have.

Before
screen shot 2017-06-28 at 6 29 46 pm

After
screen shot 2017-06-28 at 6 34 21 pm

Here is the code I used to measure: nguyenhuy@b6ff4ed. Results got from an iPhone 6S, iOS 10.3.2.

@garrettmoon
Copy link
Member

That's great! Thanks for doing the perf checks!

Copy link
Contributor

@maicki maicki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work @nguyenhuy !

@nguyenhuy
Copy link
Member Author

Landing this, thanks @garrettmoon and @maicki!

@Adlai-Holler Feel free to do a post-merge review here.

@nguyenhuy nguyenhuy merged commit c603ef0 into TextureGroup:master Jun 29, 2017
garrettmoon pushed a commit that referenced this pull request Jun 30, 2017
* Implement tests for the layout flattening process

* Refactor the flattening algorithm
- Remove flattened flag
- No more self check
- Stop traversing a layout tree branch when hits a displaynode node.
- Reuse as many existing ASLayout objects as possible

* Update changelog

* Ceil position values before comparing

* Explain why sublayout elements must be retained
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
* Implement tests for the layout flattening process

* Refactor the flattening algorithm
- Remove flattened flag
- No more self check
- Stop traversing a layout tree branch when hits a displaynode node.
- Reuse as many existing ASLayout objects as possible

* Update changelog

* Ceil position values before comparing

* Explain why sublayout elements must be retained
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants